<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>色板</title>
    <!--<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>-->
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>

<div class="basePage">

    <p></p>
    <div style="display: inline-block">
        <canvas id="platter" width="300px" height="300px"></canvas>
        <canvas id="global" style="position: absolute;visibility: hidden" width="10px" height="10px"></canvas>
    </div>

    <canvas id="colorBar" width="40px" height="300px"></canvas>

    <div>
        <span>色板：</span><span id="platterTextId"></span>
    </div>
    <div>
        <span>彩虹：</span><span id="colorBarTextId"></span>
    </div>

</div>
<script>
    var barTextEl = document.getElementById('colorBarTextId');
    var platterTextEl = document.getElementById('platterTextId');


    var Platter = (function () {
        function Platter() {
            this.sR = 0;
            this.sG = 255;
            this.sB = 255;


            this.canvas = document.getElementById('platter');
            this.ctx = this.canvas.getContext('2d');

            this.xScale = Math.ceil(this.canvas.width / 128);//向上取整方便绘制矩形
            this.yScale = Math.ceil(this.canvas.height / 128);//向上取整方便绘制矩形
            this.scaleWidth = this.canvas.width / this.xScale;
            this.scaleHeight = this.canvas.height / this.yScale;

            //xScale 和 yScale 即小色块的值，如果不取整，绘制时导致重叠变深。像素误差导致。
            // this.scaleWidth = 128;
            // this.scaleHeight = 128;
            // this.xScale = this.canvas.width / this.scaleWidth;
            // this.yScale = this.canvas.height / this.scaleHeight;


            var gCanvas = document.getElementById('global');
            var gCtx = gCanvas.getContext('2d');
            var gRadius = gCanvas.width > gCanvas.height ? gCanvas.height / 2 : gCanvas.width / 2;

            this.gCanvas = gCanvas;
            this.gCtx = gCtx;
            this.gRadius = gRadius;

            this.updateGlobal("#000000");

            var minX = this.canvas.offsetLeft - gCanvas.width / 2;
            var maxX = minX + this.canvas.offsetWidth;
            var minY = this.canvas.offsetTop - gCanvas.height / 2;
            var maxY = minY + this.canvas.offsetHeight;

            var pLeft = this.canvas.offsetLeft;
            var pTop = this.canvas.offsetTop;

            var that = this;

            var isApp = 'ontouchstart' in window;
            if (isApp) {
                // 判断后，调试时切换移动预览，需要刷新才生效
                // 如果全部绑定，移动端下，会先响应ontouchstart，再响应onmousedown

                this.canvas.ontouchstart = handleTouchEvent;
                gCanvas.ontouchstart = handleTouchEvent;
            } else {
                this.canvas.onmousedown = handleMouseEvent;
                gCanvas.onmousedown = handleMouseEvent;

            }

            function handleMouseEvent(e) {
                dragGlobal(e);
                // console.log(e);
                document.onmousemove = function (e) {
                    dragGlobal(e);
                    e.preventDefault ? e.preventDefault() : (e.returnValue = false)
                }

                document.onmouseup = function (e) {
                    document.onmousemove = null;
                    document.onmouseup = null;
                }
            }

            function handleTouchEvent(e) {
                dragGlobal(e.touches[0]);
                // console.log(e);
                document.ontouchmove = function (e) {
                    dragGlobal(e.changedTouches[0]);
                    // console.log(e);
                }
                document.ontouchend = function (e) {
                    document.ontouchmove = null;
                    document.ontouchend = null;

                }
            }


            function dragGlobal(e) {

                if ('hidden' === gCanvas.style.visibility) {
                    gCanvas.style.visibility = 'visible';
                }
                var x = e.clientX - gCanvas.width / 2;
                var y = e.clientY - gCanvas.height / 2;

                if (x < minX) {
                    x = minX;
                }
                else if (x > maxX) {
                    x = maxX;
                }
                if (y < minY) {
                    y = minY;
                }
                else if (y > maxY) {
                    y = maxY;
                }

                gCanvas.style.left = x + "px";
                gCanvas.style.top = y + "px";

                var cx = x - pLeft + gCanvas.width / 2;
                var cy = y - pTop + gCanvas.height / 2;

                var col = that.getColor(cx, cy);
                that.pickPoint = {x: cx, y: cy};

                platterTextEl.innerText = 'x:' + cx + " y:" + cy + "  rgb(" + col.r + "," + col.g + "," + col.b + ")";

                var maxCol = col.r > col.g ? col.r : (col.g > col.b ? col.g : col.b) + 1;
                if (maxCol > 128) {
                    if ("#000000" !== gCtx.strokeStyle) {
                        that.updateGlobal("#000000");
                    }
                } else {
                    if ("#dddddd" !== gCtx.strokeStyle) {
                        that.updateGlobal("#dddddd");
                    }
                }

            }
        }

        Platter.prototype.updateGlobal = function (color) {

            var gCanvas = this.gCanvas;
            var gCtx = this.gCtx;
            var gRadius = this.gRadius;

            gCtx.clearRect(0, 0, gCanvas.width, gCanvas.height);
            gCtx.strokeStyle = color;
            gCtx.beginPath();
            gCtx.arc(gRadius, gRadius, gRadius, 0, Math.PI * 2);
            gCtx.stroke();

        }

        Platter.prototype.draw = function () {
            var cw = this.canvas.width;
            var ch = this.canvas.height;

            //每个像素每个像素循环填充，计算太多，耗时严重。采取缩放，分块填充颜色的方式。当前约为128颜色级别。256以上会卡

            var xScale = this.xScale;
            var yScale = this.yScale;

            var w = this.scaleWidth;
            var h = this.scaleHeight;


            for (var x = 0; x < w; x++) {

                for (var y = 0; y < h; y++) {

                    var col = this.getScaleColor(x, y);

                    var color = 'rgb(' + col.r + ',' + col.g + ',' + col.b + ')';

                    this.ctx.fillStyle = color;
                    this.ctx.fillRect(x * xScale, y * yScale, xScale, yScale);
                }
            }

        }

        Platter.prototype.getScaleColor = function (scaleX, scaleY) {

            var w = this.scaleWidth;
            var h = this.scaleHeight;

            var r = 0;
            var g = 0;
            var b = 0;

            var sR = this.sR;
            var sG = this.sG;
            var sB = this.sB;


            var mid = sR > sG ? sR : sG;
            var max = mid > sB ? mid : sB;
            mid = mid > sB ? sB : mid;
            var min = sR + sG + sB - mid - max;

            var oneMaxXV = (255 - max) / w;
            // var oneMaxYV = (255 - max) / h;

            var oneMidXV = (255 - mid) / w;
            // var oneMidYV = (255 - mid) / h;

            var oneMinXV = (255 - min) / w;
            // var oneMinYV = (255 - min) / h;

            var midColor = 255 - scaleX * oneMidXV;
            var minColor = 255 - scaleX * oneMinXV;
            var maxColor = 255 - scaleX * oneMaxXV;


            var oneYTemp = midColor / h;
            var midC = Math.floor(midColor - scaleY * oneYTemp);

            oneYTemp = minColor / h;
            var minC = Math.floor(minColor - scaleY * oneYTemp);

            oneYTemp = maxColor / h;
            var maxC = Math.floor(maxColor - scaleY * oneYTemp);


            sR === max ? (r = maxC) : (sR === mid ? (r = midC) : (r = minC));
            sG === max ? (g = maxC) : (sG === mid ? (g = midC) : (g = minC));
            sB === max ? (b = maxC) : (sB === mid ? (b = midC) : (b = minC));

            return {r: r, g: g, b: b}

        }

        Platter.prototype.getColor = function (x, y) {
            return this.getScaleColor(x / this.xScale, y / this.yScale);
        }

        Platter.prototype.update = function (rgb) {
            this.sR = rgb.r;
            this.sG = rgb.g;
            this.sB = rgb.b;
            this.draw();
        }

        Platter.prototype.getPickColor = function () {
            if (this.pickPoint) {//选中的坐标
                return this.getColor(this.pickPoint.x, this.pickPoint.y);
            }
            return undefined;
        }

        return Platter;
    }());


    var BarHelper = (function () {

        function BarHelper() {
            this.canvas = document.getElementById('colorBar');
            this.ctx = this.canvas.getContext('2d');
            this.pickLineWidth = 10;
            this.rainWidth = this.canvas.width - 2 * this.pickLineWidth;
            this.rainHeight = this.canvas.height - this.pickLineWidth;

            var top = this.canvas.offsetTop;

            var that = this;

            var isApp = 'ontouchstart' in window;

            if (isApp) {
                // 判断后，调试时切换移动预览，需要刷新才生效
                // 如果全部绑定，移动端下，会先响应ontouchstart，再响应onmousedown（有延迟）
                this.canvas.ontouchstart = handleTouchEvent;
            } else {
                this.canvas.onmousedown = handleMouseEvent;
            }

            function handleMouseEvent(e) {
                dragLine(e);
                document.onmousemove = function (e) {
                    dragLine(e);
                    e.preventDefault ? e.preventDefault() : (e.returnValue = false)
                }

                document.onmouseup = function (event) {
                    document.onmousemove = null;
                    document.onmouseup = null;
                }
            }

            function handleTouchEvent(e) {
                dragLine(e.touches[0]);

                document.ontouchmove = function (e) {
                    dragLine(e.changedTouches[0]);
                }
                document.ontouchend = function (e) {
                    document.ontouchmove = null;
                    document.ontouchend = null;
                }
            }

            function dragLine(e) {
                var y = e.clientY - top;
                that.updatePickLine(y);
                var col = that.getPickColor();
                var color = 'rgb(' + col.r + ',' + col.g + ',' + col.b + ")";
                barTextEl.innerText = 'y:' + y + ' ' + color;
                platter.update(col);

                var col = platter.getPickColor();
                if (col) {
                    platterTextEl.innerText = "rgb(" + col.r + "," + col.g + "," + col.b + ")";
                }

            }
        }

        BarHelper.prototype.draw = function () {
            this.drawRain();
            // this.drawPickLine(0);
        }

        BarHelper.prototype.drawRain = function () {
            var w = this.rainWidth;
            var h = this.rainHeight;
            var ctx = this.ctx;
            var offY = this.pickLineWidth / 2;


            for (var y = 0; y <= h; y++) {

                var col = this.getColor(y);
                var color = 'rgb(' + col.r + ',' + col.g + ',' + col.b + ")";
                ctx.strokeStyle = color;
                // console.log(color);

                ctx.beginPath();
                ctx.moveTo(this.pickLineWidth, y + offY);
                ctx.lineTo(this.pickLineWidth + w, y + offY);
                ctx.stroke();

            }
        }

        BarHelper.prototype.getColor = function (y) {
            var h = this.rainHeight;
            //每个像素颜色级别
            var oneYV = 255 / (h / 6);

            var r = 255;
            var g = 0;
            var b = 0;
            if (y <= h / 3) {
                //红-洋红
                if (y <= h / 6) {
                    b = Math.floor(oneYV * y);
                    if (b > 255) {
                        b = 255;
                    }
                    r = 255;
                }
                //洋红-蓝
                else {
                    r = 255 - Math.floor(oneYV * (y - h / 6));
                    if (r < 0) {
                        r = 0;
                    }
                    b = 255;
                }
                g = 0;
            }
            else if (y <= 2 * h / 3) {
                if (y < 3 * h / 6) {
                    g = Math.floor(oneYV * (y - 2 * h / 6));
                    if (g > 255) {
                        g = 255;
                    }
                    b = 255;
                } else {
                    b = 255 - Math.floor(oneYV * (y - 3 * h / 6));
                    if (b < 0) {
                        b = 0;
                    }
                    g = 255;
                }
                r = 0;
            }

            else {
                if (y < 5 * h / 6) {
                    r = Math.floor(oneYV * (y - 4 * h / 6));
                    if (r > 255) {
                        r = 255;
                    }
                    g = 255;
                } else {
                    g = 255 - Math.floor(oneYV * (y - 5 * h / 6));
                    if (g < 0) {
                        g = 0;
                    }
                    r = 255;
                }
                b = 0;
            }

            return {r: r, g: g, b: b};

        }
        BarHelper.prototype.drawPickLine = function (y) {
            this.pickLineY = y;

            var w = this.pickLineWidth;
            var h = this.canvas.height - w;
            var ch = this.canvas.height;
            var cw = this.canvas.width;

            var ctx = this.ctx;

            ctx.clearRect(0, 0, w, ch);
            ctx.clearRect(cw - w, 0, w, ch);

            ctx.fillStyle = '#ffaaaa';
            ctx.beginPath();
            ctx.moveTo(0, y);
            ctx.lineTo(w, y + w / 2);
            ctx.lineTo(0, y + w);
            ctx.fill();

            ctx.beginPath();
            ctx.moveTo(cw, y);
            ctx.lineTo(cw - w, y + w / 2);
            ctx.lineTo(cw, y + w);
            ctx.fill();


        }
        BarHelper.prototype.getPickColor = function () {
            return this.getColor(this.pickLineY);
        }
        BarHelper.prototype.updatePickLine = function (y) {
            var w = this.pickLineWidth;
            var h = this.canvas.height;

            var maxY = h - w;
            var minY = 0;
            if (y < minY) {
                y = minY;
            }
            else if (y > maxY) {
                y = maxY;
            }

            this.drawPickLine(y);

        }
        return BarHelper;
    }());


    var platter = new Platter();
    platter.draw();

    var barHelper = new BarHelper();
    barHelper.draw();

</script>


</body>
</html>